////
//
/// \file   ./src/mutation_node.cpp
//
//  Copyright 2007, 2008 Chandler Carruth
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations
//  under the License.
//
////

#include <inc/parser/mutation_node.hpp>

#include <string>

#include <boost/scoped_ptr.hpp>

#include <inc/parser/expression_node.hpp>
#include <inc/parser/parse_tree_node.hpp>
#include <inc/parser/label_node.hpp>
#include <inc/parser/symbol_node.hpp>
#include <inc/parser/ws_parser.hpp>

namespace inc {
namespace parser {

using std::string;
using boost::scoped_ptr;

/// \brief  Basic constructor
/// \author Chandler Carruth
/// \date   2008.05.23

MutationNode::MutationNode()
    : ws( new WSParser )
{}

/// \brief  Basic destructor
/// \author Chandler Carruth
/// \date   2008.05.23

MutationNode::~MutationNode()
{}

/// \brief  Parse the final expression for prefix or binary mutations
/// \author Chandler Carruth
/// \date   2007.06.28

bool
MutationNode::parse_rhs_expr( const iterator& first,
                              const iterator& last,
                              iterator& last_parsed )
{
  scoped_ptr<ExpressionNode> expr( new ExpressionNode );
  if( expr->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    if( ws->parse_eol_ws( current, last, last_parsed ) )
    {
      rhs_expr.swap( expr );
      return true;
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse the symbol for a prefix mutation
/// \author Chandler Carruth
/// \date   2007.06.28

bool
MutationNode::parse_prefix_symbol( const iterator& first,
                                    const iterator& last,
                                    iterator& last_parsed )
{
  scoped_ptr<SymbolNode> symbol( new SymbolNode );
  if( symbol->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( ws->parse_ws( current, last, last_parsed ) )
      current = last_parsed;

    if( parse_rhs_expr( current, last, last_parsed ) )
    {
      mut_symbol.swap( symbol );
      return true;
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse an ambiguous double-label mutation
/// \author Chandler Carruth
/// \date   2007.06.28

bool
MutationNode::parse_label_label( const iterator& first,
                                  const iterator& last,
                                  iterator& last_parsed )
{
  scoped_ptr<LabelNode> lhs( new LabelNode );
  if( lhs->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      scoped_ptr<LabelNode> rhs( new LabelNode );
      if( rhs->parse( current, last, last_parsed ) )
      {
        current = last_parsed;
        if( ws->parse_eol_ws( current, last, last_parsed ) )
        {
          lhs_label.swap( lhs );
          rhs_label.swap( rhs );
          return true;
        }
      }
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse a label for prefix mutations
/// \author Chandler Carruth
/// \date   2007.06.28

bool
MutationNode::parse_prefix_label( const iterator& first,
                                  const iterator& last,
                                  iterator& last_parsed )
{
  scoped_ptr<LabelNode> label( new LabelNode );
  if( label->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      if( parse_rhs_expr( current, last, last_parsed ) )
      {
        mut_label.swap( label );
        return true;
      }
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse label for postfix and binary mutations
/// \author Chandler Carruth
/// \date   2007.06.28

bool
MutationNode::parse_postfix_binary_label( const iterator& first,
                                          const iterator& last,
                                          iterator& last_parsed )
{
  scoped_ptr<LabelNode> label( new LabelNode );
  if( label->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    mut_label.swap( label );

    if( ws->parse_eol_ws( current, last, last_parsed ) )
      return true;

    //  If we have whitespace, we can try and parse another expression.
    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;
      if( parse_rhs_expr( current, last, last_parsed ) )
        return true;
    }
  }

  mut_label.reset();
  last_parsed = first;
  return false;
}


/// \brief  Parse symbol for postfix and binary mutations
/// \author Chandler Carruth
/// \date   2007.06.28

bool
MutationNode::parse_postfix_binary_symbol( const iterator& first,
                                            const iterator& last,
                                            iterator& last_parsed )
{
  scoped_ptr<SymbolNode> symbol( new SymbolNode );
  if( symbol->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    mut_symbol.swap( symbol );

    if( ws->parse_eol_ws( current, last, last_parsed ) )
      return true;

    if( ws->parse_ws( current, last, last_parsed ) )
      current = last_parsed;
    if( parse_rhs_expr( current, last, last_parsed ) )
      return true;
  }

  mut_symbol.reset();
  last_parsed = first;
  return false;
}


/// \brief  Parse mutation with an expression prefix
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a mutation which begins with an expression. It then
/// branches off to handle postfix operators and binary mutations. It stores
/// the expression as a LHS expression.

bool
MutationNode::parse_lhs_expr( const iterator& first,
                              const iterator& last,
                              iterator& last_parsed )
{
  scoped_ptr<ExpressionNode> expr( new ExpressionNode );
  if( expr->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    lhs_expr.swap( expr );

    //  skip any whitespace
    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      //  since we found white space, we can check for a label operator
      if( this->parse_postfix_binary_label( current, last, last_parsed ) )
        return true;
    }

    //  try and parse out a symbol operator since we don't have a label
    //  operator.
    if( this->parse_postfix_binary_symbol( current, last, last_parsed ) )
      return true;
  }

  lhs_expr.reset();
  last_parsed = first;
  return false;
}

/// \brief  Parse a mutation, no grouping required
/// \author Chandler Carruth
/// \date   2007.06.27
///
/// This parses out a single mutation. No grouping is required as these cannot
/// be nested, each sub-component consists of an expression, and mutations only
/// occur at the statement level. The statement construct also provides all
/// whitespace delimiting necessary.

bool
MutationNode::parse( const iterator& first,
                      const iterator& last,
                      iterator& last_parsed )
{
  assert( get_text().empty() );
  assert( !mut_label );
  assert( !mut_symbol );
  assert( !lhs_expr );
  assert( !rhs_expr );
  assert( !lhs_label );
  assert( !rhs_label );

  if( parse_prefix_symbol( first, last, last_parsed ) ||
      parse_label_label( first, last, last_parsed ) ||
      parse_prefix_label( first, last, last_parsed ) ||
      parse_lhs_expr( first, last, last_parsed ) )
  {
    set_text( string( first, last_parsed ) );
    return true;
  }

  last_parsed = first;
  return false;
}

/// \brief  Accessor for an ambiguous double labels' LHS label
/// \author Chandler Carruth
/// \date   2008.05.23

LabelNode*
MutationNode::get_lhs_label() const
{
  if( lhs_label != NULL && rhs_label != NULL )
    return lhs_label.get();

  return NULL;
}

/// \brief  Accessor for an ambiguous double labels' RHS label
/// \author Chandler Carruth
/// \date   2008.05.23

LabelNode*
MutationNode::get_rhs_label() const
{
  if( lhs_label != NULL && rhs_label != NULL )
    return rhs_label.get();

  return NULL;
}

/// \brief  Accessor for a unary operations' expression
/// \author Chandler Carruth
/// \date   2008.05.23

ExpressionNode*
MutationNode::get_unary_expr() const
{
  if( lhs_expr != NULL && rhs_expr == NULL )
    return lhs_expr.get();
  if( rhs_expr != NULL && lhs_expr == NULL )
    return rhs_expr.get();

  return NULL;
}

/// \brief  Tester for prefix unary operators
/// \author Chandler Carruth
/// \date   2008.05.23

bool
MutationNode::is_unary_prefix() const
{
  return lhs_expr == NULL && rhs_expr != NULL;
}

/// \brief  Tester for postfix unary operators
/// \author Chandler Carruth
/// \date   2008.05.23

bool
MutationNode::is_unary_postfix() const
{
  return lhs_expr != NULL && rhs_expr == NULL;
}

/// \brief  Accessor for a binary operations' LHS expression
/// \author Chandler Carruth
/// \date   2008.05.23

ExpressionNode*
MutationNode::get_lhs_expr() const
{
  if( lhs_expr != NULL && rhs_expr != NULL )
    return lhs_expr.get();

  return NULL;
}

/// \brief  Accessor for a binary operations' RHS expression
/// \author Chandler Carruth
/// \date   2008.05.23

ExpressionNode*
MutationNode::get_rhs_expr() const
{
  if( lhs_expr != NULL && rhs_expr != NULL )
    return rhs_expr.get();

  return NULL;
}

/// \brief  Accessor for a label operator
/// \author Chandler Carruth
/// \date   2008.05.23

LabelNode*
MutationNode::get_mut_label() const
{
  return mut_label.get();
}

/// \brief  Accessor for a symbol operator
/// \author Chandler Carruth
/// \date   2008.05.23

SymbolNode*
MutationNode::get_mut_symbol() const
{
  return mut_symbol.get();
}

} //  end parser namespace
} //  end inc namespace
